﻿using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace OpenXmlEasy.Word
{
    public partial class WordEasy
    {
        public void AddDirecotryPlaceholder()
        {
            var paragraph1 = new Paragraph { RsidParagraphAddition = "005F46E8", RsidParagraphProperties = "005F46E8", RsidRunAdditionDefault = "005F46E8" };

            var paragraphProperties1 = new ParagraphProperties();
            {
                var indentation1 = new Indentation { FirstLine = "360", FirstLineChars = 200 };
                var paragraphMarkRunProperties1 = new ParagraphMarkRunProperties();
                paragraphProperties1.AppendChild(indentation1);
                paragraphProperties1.AppendChild(paragraphMarkRunProperties1);
            }

            var justification1 = new Justification { Val = JustificationValues.Center };

            var run1 = new Run();
            {
                var runProperties1 = new RunProperties();

                var text1 = new Text("目录");

                run1.AppendChild(runProperties1);
                run1.AppendChild(text1);
            }
            paragraph1.AppendChild(paragraphProperties1);
            paragraphProperties1.AppendChild(justification1);
            paragraph1.AppendChild(run1);

            var sdtBlock = new SdtBlock(new SdtContentBlock());

            _body.AppendChild(paragraph1);
            _body.AppendChild(sdtBlock);
        }

        public void AddDirectory()
        {
            //获取所有包含一、二级标题的段落
            var parHasStyle = _body.Descendants<Paragraph>()
                .Where(t => t.Descendants<ParagraphStyleId>().Any()
                    && t.Descendants<ParagraphStyleId>().All(c => c.Val == "heading 1" || c.Val == "heading 2"));
            const string bookMarkName = "_Toc{0}";
            var num = 988888888;
            var bookMarkAddedDic = new Dictionary<string, string>();
            foreach (var p in parHasStyle)
            {
                var bookmarkEnds = p.Descendants<BookmarkEnd>().ToArray();//获取段落中所有BookmarkEnd标签
                var bookmarkStarts = p.Descendants<BookmarkStart>().ToArray();//获取段落中所有BookmarkStart标签
                var bookmarkEndsCount = bookmarkEnds.Count();
                var bookmarkStartsCount = bookmarkStarts.Count();
                var name = string.Format(bookMarkName, ++num);
                var id = (num++).ToString(CultureInfo.InvariantCulture);

                //创建新书签用于添加到标题上下
                var bookmarkStart = new BookmarkStart { Name = name, Id = id };
                var bookmarkEnd = new BookmarkEnd { Id = id };

                if (bookmarkEndsCount == 0 && bookmarkStartsCount == 0)
                {
                    if (p.Descendants<Text>().Any())
                    {
                        AddBookMarkToParagraph(p, bookmarkEnd, bookmarkStart);//添加书签
                        bookMarkAddedDic.Add(p.Descendants<Text>().First().Text, name);//记录添加的书签
                    }
                }
                else if (bookmarkEndsCount != bookmarkStartsCount)
                {
                    DeleteBookMarkFromParagraph(_body, p, bookmarkStarts, bookmarkEnds);//删除孤单书签
                    AddBookMarkToParagraph(p, bookmarkEnd, bookmarkStart);//添加新书签
                    var dicKey = GetKey(p);//获取被添加书签的标题
                    bookMarkAddedDic.Add(dicKey, name);//记录添加的书签
                }
                AddTitleToDirecotry(p);
            }
            FixtDirectory(bookMarkAddedDic, _body);//更新目录
        }

        private void AddTitleToDirecotry(Paragraph paragraph)
        {
            var text = paragraph.Descendants<Run>().First().Descendants<Text>().First().Text;

            var sdtContentBlock = _body.Descendants<SdtBlock>().First().GetFirstChild<SdtContentBlock>();
            Paragraph paragraph1 = new Paragraph { RsidParagraphAddition = "00797775", RsidRunAdditionDefault = "00797775" };

            ParagraphProperties paragraphProperties1 = new ParagraphProperties();
            ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId { Val = "20" };

            var tabs1 = new Tabs();
            TabStop tabStop1 = new TabStop
            {
                Val = TabStopValues.Right,
                Leader = TabStopLeaderCharValues.Dot,
                Position = 9012
            };

            tabs1.AppendChild(tabStop1);

            paragraphProperties1.AppendChild(tabs1);

            if (paragraph.Descendants<ParagraphStyleId>().Any(m => m.Val == "heading 1"))
            {
                RunFonts runFonts1 = new RunFonts() { Hint = FontTypeHintValues.EastAsia };
                paragraphProperties1.AppendChild(runFonts1);
            }

            ParagraphMarkRunProperties paragraphMarkRunProperties1 = new ParagraphMarkRunProperties();
            NoProof noProof1 = new NoProof();

            paragraphMarkRunProperties1.AppendChild(noProof1);

            paragraphProperties1.AppendChild(paragraphStyleId1);
            paragraphProperties1.AppendChild(paragraphMarkRunProperties1);

            Hyperlink hyperlink1 = new Hyperlink { History = true, Anchor = "_Toc359489398" };

            Run run1 = new Run { RsidRunProperties = "008E116B" };

            RunProperties runProperties1 = new RunProperties();
            RunStyle runStyle1 = new RunStyle { Val = "a3" };
            NoProof noProof2 = new NoProof();

            runProperties1.AppendChild(runStyle1);
            runProperties1.AppendChild(noProof2);
            Text text1 = new Text { Text = text };

            run1.AppendChild(runProperties1);
            run1.AppendChild(text1);

            Run run3 = new Run();

            RunProperties runProperties3 = new RunProperties();
            NoProof noProof4 = new NoProof();
            WebHidden webHidden1 = new WebHidden();

            runProperties3.AppendChild(noProof4);
            runProperties3.AppendChild(webHidden1);
            TabChar tabChar1 = new TabChar();

            run3.AppendChild(runProperties3);
            run3.AppendChild(tabChar1);

            Run run4 = new Run();

            RunProperties runProperties4 = new RunProperties();
            NoProof noProof5 = new NoProof();
            WebHidden webHidden2 = new WebHidden();

            runProperties4.AppendChild(noProof5);
            runProperties4.AppendChild(webHidden2);
            FieldChar fieldChar1 = new FieldChar { FieldCharType = FieldCharValues.Begin };

            run4.AppendChild(runProperties4);
            run4.AppendChild(fieldChar1);

            Run run5 = new Run();

            RunProperties runProperties5 = new RunProperties();
            NoProof noProof6 = new NoProof();
            WebHidden webHidden3 = new WebHidden();

            runProperties5.AppendChild(noProof6);
            runProperties5.AppendChild(webHidden3);
            FieldCode fieldCode1 = new FieldCode
                                       {
                                           Space = SpaceProcessingModeValues.Preserve,
                                           Text = " PAGEREF _Toc359489398 \\h "
                                       };

            run5.AppendChild(runProperties5);
            run5.AppendChild(fieldCode1);

            Run run6 = new Run();

            RunProperties runProperties6 = new RunProperties();
            NoProof noProof7 = new NoProof();
            WebHidden webHidden4 = new WebHidden();

            runProperties6.AppendChild(noProof7);
            runProperties6.AppendChild(webHidden4);

            run6.AppendChild(runProperties6);

            Run run7 = new Run();

            RunProperties runProperties7 = new RunProperties();
            NoProof noProof8 = new NoProof();
            WebHidden webHidden5 = new WebHidden();

            runProperties7.AppendChild(noProof8);
            runProperties7.AppendChild(webHidden5);
            FieldChar fieldChar2 = new FieldChar { FieldCharType = FieldCharValues.Separate };

            run7.AppendChild(runProperties7);
            run7.AppendChild(fieldChar2);

            Run run8 = new Run();

            RunProperties runProperties8 = new RunProperties();
            NoProof noProof9 = new NoProof();
            WebHidden webHidden6 = new WebHidden();

            runProperties8.AppendChild(noProof9);
            runProperties8.AppendChild(webHidden6);
            Text text3 = new Text { Text = "2" };

            run8.AppendChild(runProperties8);
            run8.AppendChild(text3);

            Run run9 = new Run();

            RunProperties runProperties9 = new RunProperties();
            NoProof noProof10 = new NoProof();
            WebHidden webHidden7 = new WebHidden();

            runProperties9.AppendChild(noProof10);
            runProperties9.AppendChild(webHidden7);
            FieldChar fieldChar3 = new FieldChar { FieldCharType = FieldCharValues.End };

            run9.AppendChild(runProperties9);
            run9.AppendChild(fieldChar3);

            hyperlink1.AppendChild(run1);
            hyperlink1.AppendChild(run3);
            hyperlink1.AppendChild(run4);
            hyperlink1.AppendChild(run5);
            hyperlink1.AppendChild(run6);
            hyperlink1.AppendChild(run7);
            hyperlink1.AppendChild(run8);
            hyperlink1.AppendChild(run9);

            paragraph1.AppendChild(paragraphProperties1);
            paragraph1.AppendChild(hyperlink1);

            sdtContentBlock.AppendChild(paragraph1);
        }

        /// <summary>
        /// 将段落中文字拼起来得到标题内容
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private string GetKey(Paragraph p)
        {
            return string.Join("", p.Descendants<Text>().Select(t => t.Text));
        }

        /// <summary>
        /// 修正书签
        /// </summary>
        /// <param name="bookMarkAddedDic"></param>
        /// <param name="body"></param>
        private void FixtDirectory(Dictionary<string, string> bookMarkAddedDic, Body body)
        {
            if (bookMarkAddedDic.Count > 0)
            {
                if (body.Descendants<SdtBlock>().Any())
                {
                    //得到SdtContentBlock
                    var sdtContentBlock = body.Descendants<SdtBlock>().First().GetFirstChild<SdtContentBlock>();
                    //遍历每一个超链接，修改里面的书签值
                    foreach (var hyperlink in sdtContentBlock.Descendants<Hyperlink>())
                    {
                        var text = hyperlink.Descendants<Text>().First();//得到目录项绑定的标题内容
                        if (bookMarkAddedDic.Keys.Contains(text.Text))
                        {
                            hyperlink.Anchor = bookMarkAddedDic[text.Text];//超链接绑定到书签的name
                            var pageRef = hyperlink.Descendants<FieldCode>().First(t => t.Text.Contains("PAGEREF"));//
                            pageRef.Text = "PAGEREF " + hyperlink.Anchor + "\\h";//更新PAGEREF以更新页码
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 删除孤单标签
        /// </summary>
        /// <param name="body"></param>
        /// <param name="p"></param>
        /// <param name="bookmarkStarts"></param>
        /// <param name="bookmarkEnds"></param>
        private void DeleteBookMarkFromParagraph(Body body, Paragraph p, IEnumerable<BookmarkStart> bookmarkStarts, IEnumerable<BookmarkEnd> bookmarkEnds)
        {
            var singleStartElenmentsIn = bookmarkStarts
                .Where(t => !bookmarkEnds.Select(c => c.Id.Value).Contains(t.Id.Value))
                .ToList();

            var singleEndElenmentsIn = bookmarkEnds
                .Where(t => !singleStartElenmentsIn.Select(c => c.Id.Value).Contains(t.Id.Value));

            var singleStartElenmentsOut = body.Descendants<BookmarkStart>()
                .Where(t => singleEndElenmentsIn.Select(c => c.Id.Value).Contains(t.Id.Value));

            var singleEndElenmentsOut = body.Descendants<BookmarkEnd>()
                .Where(t => singleStartElenmentsIn.Select(c => c.Id.Value).Contains(t.Id.Value));

            //删除所有孤单标签
            Remove(singleStartElenmentsOut);
            Remove(singleEndElenmentsOut);
            Remove(singleStartElenmentsIn);
            Remove(singleEndElenmentsIn);
        }

        private void Remove(IEnumerable<OpenXmlElement> singleElenments)
        {
            singleElenments.ToList().ForEach(t => t.Remove());//删除标签
        }

        /// <summary>
        /// 添加新的标签到段落中标题上下
        /// </summary>
        /// <param name="p"></param>
        /// <param name="bookmarkEnd"></param>
        /// <param name="bookmarkStart"></param>
        private void AddBookMarkToParagraph(Paragraph p, BookmarkEnd bookmarkEnd, BookmarkStart bookmarkStart)
        {
            if (p.Descendants<Text>().Any())
            {
                var wtBegin = p.Descendants<Text>().First();
                var wtEnd = p.Descendants<Text>().Last();
                var rBegin = (Run)wtBegin.Parent;//得到标题内容开始行
                var rEnd = (Run)wtEnd.Parent;//得到标题内容结束行
                rBegin.InsertBeforeSelf(bookmarkStart);//在标题上面插入BookmarkStart
                rEnd.InsertAfterSelf(bookmarkEnd);//在标题下面插入bookmarkEnd
            }

        }


    }
}
